export default class DragSort {
  constructor(opt) {
    this.container = opt.container
      ? document.querySelector(opt.container)
      : document.body; // 拖拽元素所在容器
    this.Aims = this.container.getElementsByClassName(
      // 推拽目标元素
      opt.targetEl ? opt.targetEl : "drag-target"
    );
    this.cX = 0; // 鼠标按下x的位置
    this.cY = 0; // 鼠标按下y的位置
    this.dX = 0; // 鼠标移动x的位置
    this.dY = 0; // 鼠标移动y的位置
    this.fX = 0; // 左右移动方向
    this.fY = 0; // 上下移动方向
    this.mvDiv = null; // 移动元素
    this.moveEl = opt.moveEl || "drag-move"; // 拖拽元素class名称
    this.moveElRenderfn = opt.moveElRenderfn || null; // 自定义移动元素方法
    this.AimsPosition = []; // 目标元素位置数组
    this.AimsTarget = null; // 拖拽元素所在区域的目标元素
    this.end = opt.end ? opt.end : () => {}; // 外部传入的回调
    this.move = opt.move ? opt.move : () => {}; // 外部传入的回调
    this.drogsort = opt.drogsort || false;
    this.scrollEl = opt.scrollEl
      ? document.querySelector(opt.scrollEl)
      : document.body;
    this.bindEvent();

  }
  getOffset(el) {
    // 获取某元素以浏览器左上角为原点的坐标
    let t = 0; // 获取该元素对应父容器的上边距
    let l = 0; // 对应父容器的上边距
    // 判断是否有父容器，如果存在则累加其边距
    while (el && el.tagName !== "BODY") {
      t += el.offsetTop;
      l += el.offsetLeft;
      el = el.offsetParent;
    }
    return {
      l: l,
      t: t,
    };
  }
  getDragTarget(el) {
    // 获取可拖到元素
    let p = el;
    let a = false;
    let root = false;
    while (p && p.getAttribute && !p.getAttribute("draggable")) {
      p = p.parentNode;
      if (p === document.body) {
        root = true;
        break;
      }
    }
    a = !root && p.getAttribute && p.getAttribute("draggable") ? p : false;
    return a;
  }
  getTranslate(el) {
    const style = el.style.cssText;
    const R =
      /transform\:\s?translate3d\(([0-9]+)px\,\s?([0-9]+)px\,\s?([0-9]+)px\)/g;
    R.exec(style);
    return {
      x: Math.floor(RegExp.$1) || 0,
      y: Math.floor(RegExp.$2) || 0,
      z: Math.floor(RegExp.$3) || 0,
    };
  }
  getDragElementsPosition() {
    // 获取拖拽元素位置
    const els = this.dragsort
      ? this.container.querySelectorAll(`div[draggable="${this.dragsort}"]`)
      : this.Aims;
    this.AimsPosition = [];
    for (let index = 0; index < els.length; index++) {
      const el = els[index];
      const translate = this.getTranslate(el);
      const o = this.getOffset(el);
      
      o.l = translate.x + o.l;
      o.t = translate.y + o.t;
      this.AimsPosition.push({
        i: index,
        e: el,
        t: Math.floor(o.t),
        l: Math.floor(o.l),
        r: Math.floor(o.l + el.clientWidth),
        b: Math.floor(o.t + el.clientHeight),
        w: el.clientWidth,
        h: el.clientHeight,
        cx: Math.floor(o.l) + el.clientWidth / 2,
        cy: Math.floor(o.t) + el.clientHeight / 2,
      });
    }
    return this.AimsPosition;
  }
  increase() {
    // 拖拽元素处于目标元素内
    if (this.AimsTarget) {
      this.end(
        this.target,
        this.AimsTarget,
        this.dragStart === "sort-x" || this.dragStart === "sort-y"
      );
      this.dragsort = false;
      this.AimsTarget = null;
    }
  }
  getInside(x, y, position) {
    // 判断那个插槽区域
    let a = null;
    for (const key in position) {
      const rect = position[key];

      if (x >= rect.l && x <= rect.r && y >= rect.t && y <= rect.b) {
        a = rect;
      }
    }
    return a;
  }
  getSortIndex(x, y, position, index) {
    // 判断跟那个互换排序
    const _y = position.map((a) => a.t);
    const _x = position.map((a) => a.t);
    let i = 0;

    let a = null;
    if (this.dragsort === "sort-y") {
      i = this.getIndex(_y, y) - 1;
      if (index !== i) a = position[i];
    } else if (this.dragsort === "sort-x") {
      i = this.getIndex(_x, x) - 1;
      if (index !== i) a = position[i];
    }
    return a;
  }
  getIndex(arr, target) {
    let left = 0,
      right = arr.length - 1;
    while (left <= right) {
      let mid = Math.floor((left + right) / 2);
      if (arr[mid] <= target) {
        left = mid + 1;
      } else {
        right = mid - 1;
      }
    }
    return left; // 目标值不存在，返回可以插入的位置的负索引
  }
  bindEvent() {
    // 绑定dom拖到事件

    document.onmousedown = (e) => {
      const target = this.getDragTarget(e.target);
      if (!target) return;
      const fn = () => {
        this.target = target;
        this.dragStart(e);
        document.onmousemove = (e) => {
          this.dragMove(e);
        };
        document.onmouseup = (e) => {
          this.dragEnd(e);
        };
      };
      if (target.getAttribute("draggable") === "move") {
        this.getDragElementsPosition();
        fn();
      } else if (target.getAttribute("draggable") === "sort-x") {
        this.dragsort = "sort-x";
        this.getDragElementsPosition();
        fn();
      } else if (target.getAttribute("draggable") === "sort-y") {
        this.dragsort = "sort-y";
        this.getDragElementsPosition();
        fn();
      }
    };
  }
  dragStart(e) {
    // 拖到开始
    this.cX = e.clientX;
    this.cY = e.clientY;
    this.dX = e.clientX;
    this.dY = e.clientY;
    this.createMoveDiv();
    if (e && e.preventDefault) {
      e.preventDefault();
    } else {
      window.event.returnValue = false;
    }
  }
  dragMove(e) {
    // 拖到移动
    this.fX = e.clientX - this.cX;
    this.fY = e.clientY - this.dY;
    if (
      Math.abs(e.clientX - this.dX) > 30 ||
      Math.abs(e.clientY - this.dY) > 30
    ) {
      const xm = this.target;
      const c = this.getOffset(xm);

      const x = e.clientX - this.cX + this.getOffset(xm).l;
      const y = e.clientY - this.cY + this.getOffset(xm).t;

      this.dX = x - this.scrollEl.scrollLeft;
      this.dY = y - this.scrollEl.scrollTop;

      let sx =
        "transform: translateX(" +
        this.dX +
        "px) translateY(" +
        this.dY +
        "px);";

      if (this.dragsort === "sort-x") {
        sx = `transform: translateX(${this.dX}px)  translateY(${
          c.t - this.scrollEl.scrollTop
        }px);`;
      }
      if (this.dragsort === "sort-y") {
        sx = `transform: translateX(${
          c.l - this.scrollEl.scrollLeft
        }px) translateY(${this.dY}px);`;
      }

      this.mvDiv.style.cssText =
        "position: absolute; top: 0; left: 0; z-index: 110; height: " +
        Math.floor(xm.clientHeight) +
        "px; width: " +
        Math.floor(xm.clientWidth) +
        "px; " +
        sx;

      const AimsTarget = this.dragsort
        ? this.getSortIndex(x, y, this.AimsPosition)
        : this.getInside(x, y, this.AimsPosition, xm.getAttribute("i"));

      if (this.move) {
        this.AimsTarget = AimsTarget;
        this.move(this.target, this.AimsTarget);
      }
    }
  }
  dragEnd() {
    // 拖到结束
    document.onmouseup = null;
    document.onmousemove = null;

    if (this.mvDiv) {
      document.body.removeChild(this.mvDiv);
      this.mvDiv = null;
    }
    this.increase();
  }
  createMoveDiv() {
    // 创建移动元素
    let l = this.cX - Math.ceil(this.target.clientWidth / 2);
    let t = this.cY;
    const c = this.getOffset(this.target);
    if (Math.abs(this.cX - this.dX) < 30 || Math.abs(this.cY - this.dY) < 30) {
      l = c.l - this.scrollEl.scrollLeft;
      t = c.t - this.scrollEl.scrollTop;
    }
    if (!this.mvDiv) {
      // 未创建移动元素
      this.mvDiv = document.createElement("div");
      this.mvDiv.className = this.moveEl;
      if (this.moveElRenderfn) {
        this.mvDiv.innerHTML = this.moveElRenderfn();
      } else {
        this.mvDiv.innerHTML = this.target.outerHTML;
      }
      document.body.appendChild(this.mvDiv);
    }
    let sx = `transform: translateX(${l}px) translateY(${t}px);`;
    if (this.dragsort === "sort-x") {
      sx = `transform: translateX(${l}px)  translateY(${
        c.t - this.scrollEl.scrollTop
      }px);`;
    }
    if (this.dragsort === "sort-y") {
      sx = `transform: translateX(${
        c.l - this.scrollEl.scrollLeft
      }px) translateY(${t}px);`;
    }
    this.mvDiv.style.cssText =
      "position: absolute; top: 0; left: 0; z-index: 110;  height: " +
      this.target.clientHeight +
      "px; width: " +
      this.target.clientWidth +
      "px; " +
      sx;
  }
}
